package com.cougil.uoc.seghismed.business.impl;

import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.persistence.Query;

import com.cougil.uoc.seghismed.business.PacientService;
import com.cougil.uoc.seghismed.model.Alergia;
import com.cougil.uoc.seghismed.model.Historial;
import com.cougil.uoc.seghismed.model.Pacient;
import com.cougil.uoc.seghismed.model.Usuari;
import com.cougil.uoc.seghismed.model.ciphed.HistorialCiphed;
import com.cougil.uoc.seghismed.model.ciphed.MetgeCiphed;
import com.cougil.uoc.seghismed.model.ciphed.PacientCiphed;
import com.cougil.uoc.seghismed.model.ciphed.VisitaCiphed;
import com.cougil.uoc.seghismed.utils.SeghismedUtils;

/**
 * Classe que implementa els mètodes oferts a les peticions rebudes a través dels aplicatius dels
 * Pacients. També disposa de la lògica vinculada amb la gestió de la informació relacionada amb els
 * Pacients
 *
 * @author Ignacio Cougil Jares
 * 
 */
public class PacientServiceImpl extends CoreServerServiceImpl implements
PacientService {

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * com.cougil.uoc.seghismed.business.PacientService#consultarHistorial(com.cougil.uoc.seghismed
	 * .model.ciphed.PacientCiphed, com.cougil.uoc.seghismed.model.Usuari)
	 */
	@Override
	public HistorialCiphed consultarHistorial(PacientCiphed pacientCiphed, Usuari destinatari) {
		Pacient p = this.cipherService.desxifrarPacient(pacientCiphed, this.getGestor());
		// cerquem el pacient a través del seu DNI
		p = this.findByDni(p.getDni());
		// xifrem i signem l'historial pel pacient
		return this.cipherService.signarXifrarHistorial(p.getHistorial(), this.getGestor(), destinatari);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * com.cougil.uoc.seghismed.business.PacientService#consultarHistorialXifratPelGestor(com.cougil
	 * .uoc.seghismed.model.ciphed.PacientCiphed)
	 */
	@Override
	public Historial consultarHistorialXifratPelGestor(PacientCiphed pacient) {
		Pacient p = this.cipherService.desxifrarPacient(pacient, this.getGestor());
		// cerquem el pacient a través del seu DNI
		p = this.findByDni(p.getDni());
		// retornem l'historial tal qual sense signar per cap destinatari en concret (està xifrat pel gestor)
		return p.getHistorial();
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.PacientService#consultarPacient(com.cougil.uoc.seghismed.model.ciphed.PacientCiphed)
	 */
	@Override
	public PacientCiphed consultarPacient(PacientCiphed pCiphed) {
		Pacient pacient = this.cipherService.desxifrarPacient(pCiphed, this.getGestor());
		// cerquem el pacient a través del seu DNI
		Pacient p = this.findByDni(pacient.getDni());
		// xifrem el propi pacient emprant la clau privada del gestor i la clau pública del propi pacient
		return this.cipherService.xifrarPacient(p, this.getGestor(), p);
	}


	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.PacientService#findAlergies()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<Alergia> findAlergies() {
		CoreServerServiceImpl.logger.debug("Intentant cercar totes les alergies");
		Query query = this.em.createQuery("SELECT a FROM Alergia a");
		List<Alergia> llista = query.getResultList();
		CoreServerServiceImpl.logger.debug(llista.size()+" alergies trobades satisfactoriament");
		return llista;
	}

	private void findAndRemovePacientCiphed(Pacient pacient) {
		CoreServerServiceImpl.logger.debug("Intentant esborrar el pacient xifrat associat al pacient ["
				+ pacient.getId() + "]");
		PacientCiphed pc = this.findPacientCiphed(pacient);
		if (pc != null) {
			// eliminem finalment el pacient xifrat
			this.em.remove(pc);
			CoreServerServiceImpl.logger.debug("Pacient xifrat associat al pacient [" + pacient
					+ "] esborrat satisfactoriament");
		} else {
			CoreServerServiceImpl.logger.debug("Pacient xifrat associat al pacient [" + pacient
					+ "] no esborrat!");
		}
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.PacientService#findByDni(java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	public Pacient findByDni(String dni) {
		CoreServerServiceImpl.logger.debug("Intentant cercar el pacient amb dni [" + dni + "]");
		Pacient p = null;
		Query query = this.em.createQuery("SELECT p FROM Pacient p WHERE p.dni = :dni");
		query.setParameter("dni", dni);
		List<Pacient> llista = query.getResultList();
		if (llista.size() > 0) {
			p = llista.get(0);
			CoreServerServiceImpl.logger.debug("Pacient ["+p+"] trobat satisfactoriament");
		} else {
			CoreServerServiceImpl.logger.debug("Pacient amb DNI ["+dni+"] no trobat!");
		}
		return p;
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.PacientService#find(java.lang.Integer)
	 */
	public Pacient findById(Integer id) {
		CoreServerServiceImpl.logger.debug("Intentant cercar el pacient amb identificador [" + id
				+ "]");
		Pacient p = this.em.find(Pacient.class, id);
		if (p != null) {
			CoreServerServiceImpl.logger.debug("Pacient ["+p+"] trobat satisfactoriament");
		} else {
			CoreServerServiceImpl.logger.debug("Pacient amb identificador [" + id + "] no trobat!");
		}
		return p;
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.PacientService#findLastPacientCiphed()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public PacientCiphed findLastPacientCiphed() {
		CoreServerServiceImpl.logger.debug("Intentant cercar l'últim pacient xifrat");
		PacientCiphed pc = null;
		Query query = this.em
		.createQuery("SELECT pc FROM PacientCiphed pc WHERE pc.id = (SELECT MAX(id) FROM PacientCiphed)");
		List<PacientCiphed> llista = query.getResultList();
		if (llista.size() > 0) {
			pc = llista.get(0);
			CoreServerServiceImpl.logger.debug("Pacient xifrat [" + pc + "] trobat satisfactoriament");
		} else {
			CoreServerServiceImpl.logger.debug("Pacient xifrat no trobat!");
		}
		return pc;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see com.cougil.uoc.seghismed.business.PacientService#findPacientCiphed(java.util.List,
	 * com.cougil.uoc.seghismed.model.Pacient)
	 */
	public PacientCiphed findPacientCiphed(List<PacientCiphed> pacientsXifrats, Pacient pacient) {
		CoreServerServiceImpl.logger.debug("Intentant cercar el pacient xifrat associat al pacient ["
				+ pacient.getId() + "]");
		PacientCiphed pc = null;
		boolean trobat = false;
		for (Iterator<PacientCiphed> iterator = pacientsXifrats.iterator(); iterator.hasNext() && !trobat;) {
			pc = iterator.next();
			Pacient pDesxifrat = this.cipherService.desxifrarPacient(pc, this.getGestor());
			trobat = pDesxifrat.getId().equals(pacient.getId());
		}
		if (trobat) {
			CoreServerServiceImpl.logger.debug("Pacient xifrat associat al pacient [" + pacient.getId()
					+ "] trobat satisfactoriament");
		} else {
			CoreServerServiceImpl.logger.debug("Pacient xifrat associat al pacient [" + pacient.getId()
					+ "] no trobat!");
			pc = null;
		}
		return pc;
	}


	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.PacientService#findPacientCiphed(com.cougil.uoc.seghismed.model.Pacient)
	 */
	@Override
	public PacientCiphed findPacientCiphed(Pacient pacient) {
		CoreServerServiceImpl.logger.debug("Intentant cercar el pacient xifrat associat al pacient ["
				+ pacient.getId() + "]");
		List<PacientCiphed> pacientsXifrats = this.findPacientsCiphed();
		return this.findPacientCiphed(pacientsXifrats, pacient);
	}

	private PacientCiphed findPacientCiphedById(Integer id) {
		CoreServerServiceImpl.logger.debug("Intentant cercar el pacient xifrat amb identificador [" + id
				+ "]");
		PacientCiphed pc = this.em.find(PacientCiphed.class, id);
		if (pc != null) {
			CoreServerServiceImpl.logger.debug("Pacient xifrat [" + pc + "] trobat satisfactoriament");
		} else {
			CoreServerServiceImpl.logger.debug("Pacient xifrat amb identificador [" + id
					+ "] no trobat!");
		}
		return pc;
	}

	@SuppressWarnings("unchecked")
	private PacientCiphed findPacientCiphedByIdCiphed(byte[] idCiphed) {
		PacientCiphed pc = null;

		// TODO icj - desconec perquè el sistema no pot trobar l'element amb l'id xifrat associat.
		// En canvi si executem la query amb el valor en hexadecimal directament contra mySQL sí que
		// es retorna el valor correctament. Potser és problema de la propietat del camp BLOB vs.
		// driver mySQL

		String hexa = SeghismedUtils.bytesToHexadecimal(idCiphed);
		byte[] value = hexa.getBytes();

		// value = ("0x" + hexa).getBytes();

		// byte[] value =
		// "0x3082018e0201003182015a308201560201003081be3081b0310b3009060355040613024553311230100603550408130942617263656c6f6e61311230100603550407130942617263656c6f6e61310c300a060355040a1303554f4331163014060355040b130d50464320536567757265746174311930170603550403141043415f5046435f53656775726574617431133011060355042e130a30303030303030302d413123302106092a864886f70d01090116146361407365676869736d65642e756f632e656475020900b78475bb067530bc300d06092a864886f70d01010105000481803f64e92615eee2e0013144b4ced78b10750f926be3b8bc954ca8f137620951b8eda3867d68c02423d797ccf84af479c49afb659ac404972a61c831c58c7da272a772467b97e6506e5685c004fe8a28577b705f06a5c5e0513db76cc3e062018b159eb0d104ce97f9e946a28f2ae240e7219b537bfe84ddc8c8e496029e1f7ad5302b06092a864886f70d010701301406082a864886f70d030704081cd84b32f3c7635d80087a1e565ebf2bf871".getBytes();

		// byte[] value = pc.getIdCiphed();

		CoreServerServiceImpl.logger.debug("idCiphed: [" + hexa + "]");

		Query query = this.em.createQuery("SELECT pc FROM PacientCiphed pc WHERE idCiphed = :idCiphed");
		query.setParameter("idCiphed", value);

		List<PacientCiphed> llista = query.getResultList();
		if (llista.size() > 0) {
			pc = llista.get(0);
			CoreServerServiceImpl.logger.debug("Pacient xifrat [" + pc + "] trobat satisfactoriament");
		} else {
			CoreServerServiceImpl.logger.debug("Pacient xifrat amb idCiphed [" + idCiphed
					+ "] no trobat!");
		}
		return pc;
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.PacientService#findPacients()
	 */
	@SuppressWarnings("unchecked")
	public List<Pacient> findPacients() {
		CoreServerServiceImpl.logger.debug("Intentant cercar tots els pacients");
		Query query = this.em.createQuery("SELECT p FROM Pacient p");
		List<Pacient> llista = query.getResultList();
		CoreServerServiceImpl.logger.debug(llista.size()+" pacients trobats satisfactoriament");
		return llista;
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.PacientService#findPacientsCiphed()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<PacientCiphed> findPacientsCiphed() {
		CoreServerServiceImpl.logger.debug("Intentant cercar tots els pacients xifrats");
		Query query = this.em.createQuery("SELECT pc FROM PacientCiphed pc");
		List<PacientCiphed> llista = query.getResultList();
		CoreServerServiceImpl.logger.debug(llista.size()+" pacients xifrats trobats satisfactoriament");
		return llista;
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.PacientService#remove(java.lang.Integer)
	 */
	public void remove(Integer id) {
		CoreServerServiceImpl.logger.debug("Intentant esborrar el pacient amb identificador ["+id+"]");
		Pacient pacient = this.findById(id);
		if (pacient != null) {
			/* 	Cal esborrar aquell pacient xifrat que el seu ID coincideixi amb el que es preten esborrar.
			   	En realitat hauríem de fer servir els mètodes que venen a continuació, perqué aquest métode no és del tot
			 	eficient, donat que desxifra cadascun dels pacients xifrats un per un fins que trobi un que el seu ID
			 	coincideixi amb l'ID del pacient */

			this.findAndRemovePacientCiphed(pacient);

			/*
			 * PacientCiphed pc = this.cipherService.xifrarPacient(pacient, getGestor(),
			 * getGestor()); this.remove(pc);
			 */

			// finalment eliminem el pacient
			this.em.remove(pacient);
			CoreServerServiceImpl.logger.debug("Pacient ["+pacient+"] esborrat satisfactoriament");
		} else {
			CoreServerServiceImpl.logger.debug("Pacient ["+id+"] no esborrat!");
		}
	}

	private void remove(PacientCiphed pc) {
		CoreServerServiceImpl.logger.debug("Intentant esborrar el pacient xifrat [" + pc + "]");
		pc = this.findPacientCiphedByIdCiphed(pc.getIdCiphed());
		if (pc != null) {
			this.em.remove(pc);
			CoreServerServiceImpl.logger.debug("Pacient xifrat [" + pc + "] esborrat satisfactoriament");
		} else {
			CoreServerServiceImpl.logger.debug("Pacient xifrat [" + pc + "] no esborrat!");
		}
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.PacientService#removeAll()
	 */
	public int removeAll() {
		CoreServerServiceImpl.logger.debug("Intentant esborrar tots els pacients");
		/* @see com.cougil.uoc.seghismed.business.impl.MetgeServiceImpl#removeAll for more info about this solution */
		List<Pacient> llista = this.findPacients();
		int i = 0;
		for (Iterator<Pacient> iterator = llista.iterator(); iterator.hasNext();) {
			Pacient pacient = iterator.next();
			this.remove(pacient.getId());
			i++;
		}
		CoreServerServiceImpl.logger.debug(i+" pacients esborrats satisfactoriament");
		return i;
	}

	private void save(Historial historial) {
		CoreServerServiceImpl.logger.debug("Intentant emmagatzemar l'historial: " + historial);
		 if (historial != null) {
			// this.saveMetgesXifrats(historial.getMetges());
			// this.saveVisitesProtegides(historial.getVisitesProtegides());
			if (historial.getId() == null) {
				this.em.persist(historial);
			} else {
				this.em.merge(historial);
			}
			CoreServerServiceImpl.logger.debug("Historial [" + historial
					+ "] actualitzat satisfactoriament amb " + historial.sizeMetges() + " metges i "
					+ historial.sizeVisites() + " visites");
		} else {
			CoreServerServiceImpl.logger.debug("Pacient sense historial");
		}
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.PacientService#save(com.cougil.uoc.seghismed.model.Pacient)
	 */
	public void save(Pacient pacient) {
		CoreServerServiceImpl.logger.debug("Intentant emmagatzemar el pacient: " + pacient);

		// nou pacient
		if (pacient.getId() == null) {
			if (pacient.getDataAlta() == null) {
				pacient.setDataAlta(new Date());
			}
			this.em.persist(pacient);
			CoreServerServiceImpl.logger.debug("Pacient ["+pacient+"] emmagatzemat satisfactoriament");

			// cal instanciar un objecte PacientCiphed que representa el pacient xifrat només pel gestor
			// PacientCiphed pc = this.cipherService.signarXifrarPacient(pacient, this.getGestor(),
			// this.getGestor());
			PacientCiphed pc = this.cipherService.xifrarPacient(pacient, this.getGestor(), this.getGestor());
			this.save(pc);

			// sinó, el pacient ja existeix, l'actualitzem
		} else {
			pacient.setDataModificacio(new Date());
			this.em.merge(pacient);
			CoreServerServiceImpl.logger.debug("Pacient ["+pacient+"] actualitzat satisfactoriament");
		}

		// guardem el seu historial
		this.save(pacient.getHistorial());


	}

	private void save(PacientCiphed pc) {
		CoreServerServiceImpl.logger.debug("Intentant emmagatzemar el pacient xifrat: " + pc);
		if (pc.getId() == null) {
			this.em.persist(pc);
			CoreServerServiceImpl.logger.debug("Pacient xifrat ["+pc+"] emmagatzemat satisfactoriament");

		} else {
			this.em.merge(pc);
			CoreServerServiceImpl.logger.debug("Pacient xifrat ["+pc+"] actualitzat satisfactoriament");
		}
	}

	private void saveMetgesXifrats(Set<MetgeCiphed> metges) {
		CoreServerServiceImpl.logger
		.debug("Intentant emmagatzemar la col·leció de metges xifrats: " + metges);
		for (Iterator<MetgeCiphed> iterator = metges.iterator(); iterator.hasNext();) {
			MetgeCiphed metgeCiphed = iterator.next();
			// nou metge xifrat
			if (metgeCiphed.getId() == null) {
				this.em.persist(metgeCiphed);
				CoreServerServiceImpl.logger.debug("Metge xifrat ["+metgeCiphed+"] emmagatzemat satisfactoriament");
				// el metge ja existeix, l'actualitzem
			} else {
				this.em.merge(metgeCiphed);
				CoreServerServiceImpl.logger.debug("Metge xifrat ["+metgeCiphed+"] actualitzat satisfactoriament");
			}
		}
	}

	private void saveVisitesProtegides(Set<VisitaCiphed> visites) {
		CoreServerServiceImpl.logger
		.debug("Intentant emmagatzemar la col·leció de visites xifrades: " + visites);
		for (Iterator<VisitaCiphed> iterator = visites.iterator(); iterator.hasNext();) {
			VisitaCiphed visitaCiphed = iterator.next();
			// nova visita xifrada
			if (visitaCiphed.getId() == null) {
				// Visita visita = this.cipherService.verificarDesxifrarVisita(visitaCiphed,
				// this.getGestor());
				// this.visitaService.save(visita);
				// VisitaCiphed vc = this.visitaService.findVisitaCiphed(visita);
				// visites.remove(visitaCiphed);
				// visites.add(vc);
				this.em.persist(visitaCiphed);
				CoreServerServiceImpl.logger.debug("Visita xifrada [" + visitaCiphed
						+ "] emmagatzemada satisfactoriament");
				// la visita ja existeix, l'actualitzem
			} else {
				this.em.merge(visitaCiphed);
				CoreServerServiceImpl.logger.debug("Visita xifrada [" + visitaCiphed
						+ "] actualitzada satisfactoriament");
			}
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see com.cougil.uoc.seghismed.business.PacientService#totalPacientsCiphed()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public long totalPacientsCiphed() {
		CoreServerServiceImpl.logger.debug("Intentant comptar tots els pacients xifrats");
		Query query = this.em.createQuery("SELECT COUNT(*) FROM PacientCiphed");
		List<Long> llista = query.getResultList();
		long resultat = llista.get(0).longValue();
		CoreServerServiceImpl.logger.debug(resultat + " pacients xifrats comptats");
		return resultat;
	}



}
